#include "Socket.h"

Socket::~Socket()
{
	if(sockfd!=-1)
		close(sockfd);
}

Socket::Socket(const std::string &host, port_type port)
{
	conectado = false;
	struct hostent *he = NULL;
	// Obtencion de la IP del servidor
	if ((he = gethostbyname(host.c_str())) != NULL)
	{
		// Apertura del socket, obtencion del file descriptor
		if ((sockfd = socket(AF_INET, SOCK_STREAM,0))!=-1)
		{
			// Datos del servidor
			struct sockaddr_in destino;
			destino.sin_family = AF_INET;
			destino.sin_port = htons (port);
			destino.sin_addr = *((struct in_addr *)he->h_addr);
			memset(&(destino.sin_zero),8,sizeof(int));
			// Intento de conexion con el servidor
			if (connect(sockfd, (struct sockaddr *)&destino, sizeof(struct sockaddr))!=-1)
			    conectado = true;
		}
	}
}

Socket::Socket(port_type port, unsigned int backlog)
{
	conectado = false;
	struct sockaddr_in server_addr;
	// apertura del socket
    if ((sockfd = socket(AF_INET, SOCK_STREAM,0)) != -1)
    {
    	memset((char*)&server_addr, 0, sizeof(server_addr));
    	server_addr.sin_family = AF_INET;
    	server_addr.sin_port = htons(port);
    	server_addr.sin_addr.s_addr = INADDR_ANY;
    	// se bindea el socket
    	if (bind(sockfd, (struct sockaddr*)&server_addr, (socklen_t)sizeof(struct sockaddr))!=-1)
            // se deja el socket escuchando conexiones en el puerto 'port'
            if (listen(sockfd, backlog) != -1)
            	conectado = true;
    }	
}

int Socket::send(const std::string& buffer)
{
	int escrito=0, cantEnviada=0, longitud=buffer.size();
	const char* datos = buffer.c_str();
	// envia hasta completar la longitud del mensaje
	while (conectado && escrito<longitud)
	{
		cantEnviada = ::send(sockfd, datos+escrito, longitud-escrito, 0);
		if (cantEnviada >0)
			escrito += cantEnviada;
		else
			// si no se envia se sale del ciclo
			if (cantEnviada==0 || cantEnviada==-1)
			    conectado = false;
	}
	return escrito;
}

int Socket::recv(std::string& buffer, size_t len)
{
	int leido=0, cantEnviada=0;
	char* datos = new char[len+1];
	// se lee del socket hasta completar la longitud 'len'
	while (conectado && leido<(int)len)
	{
		cantEnviada = ::recv(sockfd, datos+leido, len-leido, 0);
		if (cantEnviada>0)
		    leido += cantEnviada;
		else
		{
			// si no se recibe se sale del ciclo
			if (cantEnviada==0 || cantEnviada == -1)
				conectado = false;
		}
	}
	buffer.clear();
	if (conectado)
	    buffer.assign(datos, len); 
	delete []datos;
	return leido;
}

Socket* Socket::accept()
{
	struct sockaddr cliente_addr;
	socklen_t clilen = (socklen_t)sizeof(cliente_addr);
	int sockfdCliente;
    if ((sockfdCliente = ::accept(sockfd, &cliente_addr, &clilen)) != -1)
        return new Socket(sockfdCliente);
    else
    	return NULL;	
}

Socket::operator bool () const
{
	return conectado;	
}

Socket::Socket(int fd)
{
	sockfd = fd;
	conectado = true;
}
